package com.tnsw.facebookautoliker.helper;

import java.net.HttpURLConnection;
import java.util.Hashtable;
import java.util.UUID;

import com.facebook.FacebookException;
import com.facebook.FacebookServiceException;
import com.facebook.Request;
import com.facebook.RequestBatch;
import com.facebook.Response;
import com.facebook.Session;
import com.tnsw.coreutils.MyLog;
import com.tnsw.facebookautoliker.R;
import com.tnsw.facebookautoliker.base.FragmentBase;
import com.tnsw.facebookautoliker.services.AutoLikerService;

import android.app.Activity;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;

public abstract class FacebookCommandObject {

	public static int READ_TIMEOUT = 15000;				// millisec
	public static int CONNECTION_TIMEOUT = 5000;		// millisec
	
	protected String tag = "FacebookCommandObject";
	protected Context context;
	private String _requestId;
	private FacebookCommandObject _parent;
	private int _childrenCommandCount = 0;
	private int minFrequenceMillisecond = -1;
	
	private boolean _applyCounter = true;
	private boolean _willRedirect = true;
	
	private Response _response = null;
	protected FacebookBGManager fbm;
	private long executeTime;
	private FragmentBase parentFragmentBase = null;
	private Hashtable<String, Object> hashTags;
	
	protected boolean _requiredSignature = false;
	private Exception _lastException = null;
	
	private long maxWaitMil = 10000;		// max wait for this command, exceed this will timeout
	private int readTimeout = READ_TIMEOUT;
	private int connectionTimeout = CONNECTION_TIMEOUT;
	private boolean _isRequiredFacebookSession = true;
	
	public FacebookCommandObject(Context context) {
		this.context = context;
		this._requestId = UUID.randomUUID().toString();
		this.hashTags = new Hashtable<String, Object>();
		this.fbm = FacebookBGManager.getInstance(context);
	}
	
	public FacebookCommandObject(Context context, FacebookCommandObject parent) {
		this.context = context;
		this._requestId = UUID.randomUUID().toString();
		this._parent = parent;
		this.hashTags = new Hashtable<String, Object>();
		this.fbm = FacebookBGManager.getInstance(context);
	}
	
	public boolean isRequiredFacebookSession() {
		return(this._isRequiredFacebookSession);
	}
	
	public void setRequiredFacebookSession(boolean b) {
		this._isRequiredFacebookSession = b;
	}
	
	public void setReadTimeout(int t) {
		this.readTimeout = t;
	}
	
	public int getReadTimeout() {
		return(this.readTimeout);
	}
	
	public void setConnectionTimeout(int t) {
		this.connectionTimeout = t;
	}
	
	public int getConnectionTimeout() {
		return(this.connectionTimeout);
	}
	
	public void setMaxWaitMil(long m) {
		this.maxWaitMil = m;
	}
	
	public long getMaxWaitMil() {
		return(this.maxWaitMil);
	}
	
	public void setMinFrequenceMillisecond(int mil) {
		this.minFrequenceMillisecond = mil;
	}
	
	public int getMinFrequenceMillisecond() {
		return(this.minFrequenceMillisecond);
	}
	
	public Exception getLastException() {
		return(this._lastException);
	}
	
	public String getSignature() {
		return(this.getClass().getName());
	}
	
	public boolean requiredSignature() {
		return(this._requiredSignature);
	}
	
	public void setRequiredSignature(boolean b) {
		this._requiredSignature = b;
	}
	
	public void clearTag() {
		this.hashTags.clear();
	}
	
	public boolean removeTag(String key) {
		if (this.hashTags.containsKey(key) == false) return(false);
		
		this.hashTags.remove(key);
		
		return(true);
	}
	
	public void setTag(String key, Object value) {
		this.hashTags.put(key, value);
	}
	
	public Object getTag(String key) {
		return(this.hashTags.get(key));
	}
	
	public boolean hasTag(String key) {
		return(this.hashTags.containsKey(key));
	}
	
	public void setParentFragmentBase(FragmentBase fgb) {
		this.parentFragmentBase = fgb;
	}
	
	public void setRedirectStatus(boolean b) {
		this._willRedirect = b;
	}
	
	public boolean getRedirectStatus() {
		return(this._willRedirect);
	}
	
	protected void setResponse(Response response) {
		this._response = response;
	}
	
	public Response getResponse() {
		return(this._response);
	}
	
	public boolean applyCounter() {
		return(this._applyCounter);
	}
	
	public void setApplyCounter(boolean b) {
		MyLog.d(tag, "_applyCounter=" + b);
		this._applyCounter = b;
	}
	
	/**
	 * This must be set before .execute()
	 * 
	 * @param count
	 */
	public void setChildrenCommandCount(int count) {
		this._childrenCommandCount = count;
	}
	
	public int getChildrenCommandCount() {
		return(this._childrenCommandCount);
	}
	
	public void setParent(FacebookCommandObject parent) {
		this._parent = parent;
	}
	
	public FacebookCommandObject getParent() {
		return(this._parent);
	}
	
	public String requestId() {
		return(this._requestId);
	}
		
	public long getExecuteTime() {
		return(this.executeTime);
	}
	
	/**
	 * If return false, execute() will not be called.
	 * @return
	 */
	protected boolean prepare() {
		return(true);
	}
	
	public void execute() {
		Session session = Session.openActiveSessionFromCache(this.context);
		if (this._isRequiredFacebookSession == true) {
			if (session == null) {
				MyLog.e(tag, "This command required Facebook Session but cannot get any from Session.openActiveSessionFromCache(), need to re-login again");
				if (this._willRedirect == true) {
					if (this.context instanceof Activity) {
						// Handle as Activity (By User Interface)
						Intent intent = new Intent(this.context, com.tnsw.facebookautoliker.MainActivity.class);
						intent.putExtra("INSIDE", "YES");
						
						Activity aa = (Activity) this.context;
						aa.startActivityForResult(intent, 402);	
					} else {
						MyLog.d(tag, "Context who sends error is Service, need to handle somewhere else");
						this.addNotification();
						
					}
				}
			}
		}
		
		this.executeTime = System.currentTimeMillis();
		if (this.parentFragmentBase != null) {
			this.parentFragmentBase.setLastCommand(this);
			this.parentFragmentBase.onFacebookCommandExecute(this);
		}
		
		this.fbm.addCommand(this);
		this.fbm.increaseCounter(this);
	}
	
	protected void standardExecute(Request req) {
		if (GeneralManager.isUiThread() == true) {
			
			HttpURLConnection h = Request.toHttpConnection(req);
			h.setReadTimeout(this.readTimeout);
			h.setConnectTimeout(this.connectionTimeout);
			RequestBatch rb = new RequestBatch(req);
			Request.executeConnectionAsync(h, rb);
				
		} else {
			
			HttpURLConnection h = Request.toHttpConnection(req);
			h.setReadTimeout(this.readTimeout);
			h.setConnectTimeout(this.connectionTimeout);
			RequestBatch rb = new RequestBatch(req);
			Request.executeConnectionAndWait(h, rb);
		}
	}
	
	@Override
	public String toString() {
		long now = System.currentTimeMillis();
		long duration = (now-this.executeTime)/1000;
		
		String ss = this.getClass().getName() + ";" + duration + " sec;requestId=" + this.requestId();
		
		return(ss);
	}
	
	private void addNotification() {
		MyLog.d(tag, "addNotification()");
		NotificationManager nm = (NotificationManager) this.context.getSystemService(Context.NOTIFICATION_SERVICE);
		if (nm != null) {
			Notification.Builder nb = new Notification.Builder(this.context);
			nb.setContentTitle("AutoLiker");
			nb.setContentText("Facebook Authentication failed:" + System.currentTimeMillis());
			nb.setSmallIcon(R.drawable.ic_launcher);
			
			Intent intent = new Intent(this.context, com.tnsw.facebookautoliker.MainActivity.class);
			intent.putExtra("INSIDE", "YES");

			PendingIntent pIntent = PendingIntent.getActivity(this.context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);			
			nb.setContentIntent(pIntent);
			
			Notification n = nb.build();
			
			nm.notify("AutoLiker", 1, n);
			MyLog.d(tag, "addNotification() - notified");
		}
	}
	
	protected void handleResult(Response response) {
		// isUIThread = false
		
		if (this.fbm.isRequestIdValid(this._requestId) == false) {
			MyLog.d(tag, "requestId=" + this._requestId + " is invalid, may be cancalled.");
			return;
		}
		
		if (response != null) {
			if (response.getError() != null) {
				if (response.getError().getException() != null) {
					FacebookException e = response.getError().getException();
					this._lastException = e;
					/*
					 * MyLog.e(tag, "handleResult(getCause) - " + e.getCause().getClass().getName(), e);
					 * java.net.ConnectException 
					 * java.io.EOFException
					 */
					
					MyLog.e(tag, "handleResult - " + e.toString(), e);
															
					this.fbm.updateCommandStatus(this, 400, response.getError().getException());
					
					if (this._willRedirect == true) {
						if (e instanceof FacebookServiceException) {
							MyLog.e(tag, "Yes, it is FacebookServiceException");
							
							if (response.getError().shouldNotifyUser() == true) {
								MyLog.e(tag, "Should Notify user");
								String userAction = this.context.getString(response.getError().getUserActionMessageId());
								MyLog.e(tag, "userAction=" + userAction);
							} else {
								MyLog.e(tag, "Should not Notify user");
							}
							
							switch (response.getError().getCategory()) {
								case AUTHENTICATION_RETRY:
									MyLog.e(tag, "Error: AUTHENTICATION_RETRY");
									
									break;
									
								case AUTHENTICATION_REOPEN_SESSION:
									MyLog.e(tag, "Error: AUTHENTICATION_REOPEN_SESSION");
									// When user manually remove App from App Settings in Facebook
									break;
									
								case THROTTLING:
									MyLog.d(tag, "Error: THROTTLING");
									break;
									
								default:
									MyLog.d(tag, "DEFAULT:" + response.getError().getCategory().toString() + "(" + response.getError().getCategory() + ")");
									break;
							}
													
							Intent intent = new Intent(this.context, com.tnsw.facebookautoliker.MainActivity.class);
							intent.putExtra("INSIDE", "YES");
							
							if (this.context instanceof Activity) {
								Activity aa = (Activity) this.context;
								aa.startActivityForResult(intent, 402);	
							} else if (this.context instanceof IntentService) {
								MyLog.d(tag, "Context who sends error is Service, need to handle somewhere else");
								this.addNotification();
								
								/*
								MyLog.d(tag, "trying to redirect current activity to MainAtivity");
								AutoLikerService aService = AutoLikerService.getInstance();
								if (aService.getCurrentActivity() != null) {
									aService.getCurrentActivity().startActivityForResult(intent, 402);
								}
								*/
							}
							
							
						} else {
							MyLog.d(tag, "Redirecting to ConnectionProblemActivity..");
							
							if (this.context instanceof Activity) {
								Intent intent = new Intent(this.context, com.tnsw.facebookautoliker.ConnectionProblemActivity.class);
								Activity a = (Activity) this.context;
								a.startActivityForResult(intent, 401);
								
							} else if (this.context instanceof IntentService) {
								MyLog.d(tag, "Context who sends error is Service, need to handle somewhere else");
							}
							
						}
					
						return;
						
					}
					
				}
			}
			
		} else {  // Somehow, response is null!!
			
		}
	}
}
